﻿/****************************************************************************************************************
*                                                                                                               *
* Copyright (C) 2011 5173.com                                                                                   *
* This project may be copied only under the terms of the Apache License 2.0.                                    *
* Please visit the project Home Page http://bqqapicsharp.codeplex.com/ for more detail.                         *
*                                                                                                               *
****************************************************************************************************************/

namespace BQQAPIClient.Core
{
    using System;
    using System.Runtime.CompilerServices;
    using System.Threading;
    using BQQAPIClient.Core.Business;
    using BQQAPIClient.Core.Network;
    using BQQAPIClient.Core.Protocol;
    using BQQAPIClient.Core.Utility;

    public class BQQAPI
    {
        private static Server server = new Server();
        private static Timer heartBeatTimer;
        private static readonly object locker = new object();
        private static DateTime lastHeartBeatTimeResponseTime = DateTime.Now;
        private static uint SeqNum = 1;
        private static bool enable = true;

        internal static uint GetSeqNum()
        {
            lock (locker)
                SeqNum++;
            return SeqNum;
        }

        public static Func<uint, ushort> AssignKefuFunc;
        public static Action<KefuStatusList> GetAllKefuStatusAction;
        public static Action<KefuStatusList> KefuStatusNotifyAction;
        internal static Action<UserVisit> UserVisistNotifyAction;
        internal static Action<HeartBeat> S2CHeartBeatNotifyAction;
        internal static Action C2SHeartBeatNotifyAction;

        static BQQAPI()
        {
            SeqNum = (uint)(((TimeSpan)(DateTime.Now - DateTime.Parse("2011/1/1"))).TotalSeconds);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        }

        public static ServerPerformanceCounter ServerPerformanceCounter
        {
            get
            {
                return server.ServerPerformanceCounter;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Start()
        {
            if (AssignKefuFunc == null)
                AssignKefuFunc = qq => 1001;

            UserVisistNotifyAction = userVisit =>
            {
                ushort kefuID = BQQAPI.AssignKefuFunc(userVisit.UserQQ);

                BQQAPI.AssignKefu(new AssignKefu
                {
                    AssignMode = AssignModeEnum.Kefu,
                    KefuID = kefuID,
                    QQID = userVisit.UserQQ,
                });
            };
            S2CHeartBeatNotifyAction = S2CHeartBeat;
            C2SHeartBeatNotifyAction = () => lastHeartBeatTimeResponseTime = DateTime.Now;

            server.Start(GetNextDaemon());
            heartBeatTimer = new Timer(HeartBeatAction, null, server.ServerConfiguration.C2SHeartbeatInterval, server.ServerConfiguration.C2SHeartbeatInterval);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void Stop()
        {
            server.Stop();
            Logger.Dispose();
            enable = false;
            heartBeatTimer = null;
        }

        public static void InformDaemon()
        {
            Logger.Info("SetDaemon");
            var package = new SetDaemonPackage();
            server.StartSend(package.GetBytes());
        }

        public static void SetKefuStatus(KefuStatusList kefuStatusList)
        {
            Logger.Debug("SetKefuStatus");
            var package = new SetKefuStatusPackage(kefuStatusList);
            server.StartSend(package.GetBytes());
        }

        public static void SetKefuStatus(KefuStatus kefuStatus)
        {
            SetKefuStatus(new KefuStatusList { kefuStatus });
        }

        public static void GetAllKefuStatus()
        {
            Logger.Debug("GetAllKefuStatus");
            var package = new GetAllKefuStatusPackage();
            server.StartSend(package.GetBytes());
        }

        private static void HeartBeatAction(object state)
        {
            if (enable)
            {
                C2SHeartBeat(new HeartBeat { SeqNum = GetSeqNum() });
                if ((DateTime.Now - lastHeartBeatTimeResponseTime).TotalMilliseconds > server.ServerConfiguration.C2SHeartbeatInterval * 2)
                {
                    enable = false;
                    lastHeartBeatTimeResponseTime = DateTime.Now.AddMilliseconds(server.ServerConfiguration.C2SHeartbeatInterval);
                    var daemon = GetNextDaemon();
                    Logger.Warning("Daemon is unavailable, will switch to " + daemon);
                    server.Stop();
                    InformDaemon();
                    server.Start(daemon);
                    enable = true;
                }
            }
        }

        private static Daemon GetNextDaemon()
        {
            var daemon = server.ServerConfiguration.DaemonList.Dequeue();
            server.ServerConfiguration.DaemonList.Enqueue(daemon);
            return daemon;
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Logger.Error(e.ExceptionObject.ToString());
        }

        private static void C2SHeartBeat(HeartBeat heartBeat)
        {
            Logger.Info("C2SHeartBeat");
            var package = new C2SHeartBeatPackage(heartBeat);
            server.StartSend(package.GetBytes());
        }

        private static void S2CHeartBeat(HeartBeat heartBeat)
        {
            Logger.Info("S2CHeartBeat");
            var package = new S2CHeartBeatPackage(heartBeat);
            server.StartSend(package.GetBytes());
        }

        private static void AssignKefu(AssignKefu assignKefu)
        {
            Logger.Debug("AssignKefu");
            var package = new AssignKefuPackage(assignKefu);
            server.StartSend(package.GetBytes());
        }
    }
}
